package com.dailywork.basepro.xml;

import com.common.constants.Constants;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.*;
import javax.xml.stream.events.XMLEvent;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.IOException;
import java.io.InputStream;

/**
 * xml流式处理方式
 *
 * @author think
 * @version 2019/10/20
 * @since xml流式处理方式
 */
public class StreamParseXml {

    private static Logger logger = Logger.getLogger(StreamParseXml.class);

    /**
     * 基于光标模型：
     *
     * <p>获取xml文件的元素和文本内容；
     */
    public void schemeOne(String fileName) {
        // 实例化一个xml输入流工厂，创建一个xml文件读取器；
        XMLInputFactory factory = XMLInputFactory.newInstance();
        InputStream is = null;
        try {
            // 获取xml文件的的输入流；
            is = StreamParseXml.class.getClassLoader().getResourceAsStream(fileName);
            // 创建一个XMLStreamReader，读取xml文件，获取相应内容；
            XMLStreamReader reader = factory.createXMLStreamReader(is);
            while (reader.hasNext()) {
                int type = reader.next();
                if (type == XMLStreamConstants.START_ELEMENT) {
                    System.out.println(reader.getName());
                } else if (type == XMLStreamConstants.CHARACTERS) {
                    System.out.println(reader.getText().trim());
                } else if (type == XMLStreamConstants.END_ELEMENT) {
                    System.out.println("/" + reader.getName());
                }
            }
        } catch (XMLStreamException e) {
            e.printStackTrace();
        } finally {
            try {
                // 关闭读取xml文件的输入流；
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 基于光标模型：
     *
     * <p>获取指定元素名的属性名和属性值；
     */
    public void schemeTwo(String fileName) {
        XMLInputFactory factory = XMLInputFactory.newInstance();
        InputStream is = null;
        try {
            is = StreamParseXml.class.getClassLoader().getResourceAsStream(fileName);
            XMLStreamReader reader = factory.createXMLStreamReader(is);
            while (reader.hasNext()) {
                int type = reader.next();
                if (type == XMLStreamConstants.START_ELEMENT) {
                    String name = reader.getName().toString();
                    // 获取元素名称为book的第一个属性名和属性值；
                    if ("book".equals(name)) {
                        System.out.println(reader.getAttributeName(0) + ":" + reader.getAttributeValue(0));
                    }
                }
            }
        } catch (XMLStreamException e) {
            e.printStackTrace();
        } finally {
            try {
                // 关闭读取xml文件的输入流；
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 基于光标模型：
     *
     * <p>获取xml文件指定元素的文本内容(元素的值)
     */
    public void schemeThree(String fileName) {
        XMLInputFactory factory = XMLInputFactory.newInstance();
        InputStream is = null;
        try {
            is = StreamParseXml.class.getClassLoader().getResourceAsStream(fileName);
            XMLStreamReader reader = factory.createXMLStreamReader(is);
            while (reader.hasNext()) {
                int type = reader.next();

                if (type == XMLStreamConstants.START_ELEMENT) {
                    String name = reader.getName().toString();
                    // 获取名为"title"的元素的文本内容(元素的值)
                    if ("title".equals(name)) {
                        System.out.print(reader.getElementText() + ":");
                    }
                    // 获取名为"price"的元素的文本内容(元素的值)
                    if ("price".equals(name)) {
                        System.out.print(reader.getElementText() + "\n");
                    }
                }
            }
        } catch (XMLStreamException e) {
            e.printStackTrace();
        } finally {
            try {
                // 关闭读取xml文件的输入流；
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 基于迭代模型：
     *
     * <p>获取xml文件指定元素的文本内容(元素的值)
     *
     * <p>迭代次数：本次测试时85；
     *
     * <p>根据迭代次数和过滤模型相比，过滤模型的远高于迭代模型的；
     */
    public void schemeFour(String fileName) {
        XMLInputFactory factory = XMLInputFactory.newInstance();
        InputStream is = null;
        try {
            is = StreamParseXml.class.getClassLoader().getResourceAsStream(fileName);
            // 基于迭代模型的操作方式
            XMLEventReader reader = factory.createXMLEventReader(is);
            xmlEventDeal(reader);
        } catch (XMLStreamException e) {
            e.printStackTrace();
        } finally {
            try {
                // 关闭读取xml文件的输入流；
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void xmlEventDeal(XMLEventReader reader) throws XMLStreamException {
        int num = 0;
        while (reader.hasNext()) {
            // 通过XMLEvent来获取是否是某种节点类型
            XMLEvent event = reader.nextEvent();
            if (event.isStartElement()) {
                // 通过event.asxxx转换节点类型；
                String name = event.asStartElement().getName().toString();
                if ("title".equals(name)) {
                    System.out.print(reader.getElementText() + ":");
                }
                if ("price".equals(name)) {
                    System.out.print(reader.getElementText() + "\n");
                }
            }
            num++;
        }
    }

    /**
     * 基于过滤模型：
     *
     * <p>获取xml文件指定元素的文本内容(元素的值)
     *
     * <p>加过滤器后的迭代次数：本次测试结果：8；
     *
     * <p>所以可以得到的结果是：使用过滤模型的效率远高于迭代模型；
     */
    public void schemeFive(String fileName) {
        XMLInputFactory factory = XMLInputFactory.newInstance();
        InputStream is = null;
        try {
            is = StreamParseXml.class.getClassLoader().getResourceAsStream(fileName);
            // 基于Filter的过滤方式，可以有效的过滤掉不用进行操作的节点，效率会高一些
            EventFilter eventFilter =
                    (XMLEvent event) -> {
                        // 返回true表示会显示，返回false表示不显示
                        if (event.isStartElement()) {
                            String name = event.asStartElement().getName().toString();
                            return Constants.TITLE.equals(name) || Constants.PRICE.equals(name);
                        }
                        return false;
                    };
            XMLEventReader reader =
                    factory.createFilteredReader(factory.createXMLEventReader(is), eventFilter);
            xmlEventDeal(reader);
        } catch (XMLStreamException e) {
            e.printStackTrace();
        } finally {
            try {
                // 关闭读取xml文件的输入流；
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 基于XPath ：XPath 是一门在 XML 文档中查找信息的语言；
     */
    public void schemeSix() {
        InputStream is = null;
        try {
            is = StreamParseXml.class.getClassLoader().getResourceAsStream("com/zp/ws/test03/books.xml");
            // 创建文档处理对象
            DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            // 通过DocumentBuilder创建doc的文档对象
            assert is != null;
            Document doc = db.parse(is);
            // 创建XPath
            XPath xpath = XPathFactory.newInstance().newXPath();
            // 第一个参数就是xpath,第二参数就是文档
            NodeList list =
                    (NodeList) xpath.evaluate("//book[@category='WEB']", doc, XPathConstants.NODESET);
            for (int i = 0; i < list.getLength(); i++) {
                // 遍历输出相应的结果
                Element e = (Element) list.item(i);
                System.out.println(e.getElementsByTagName("title").item(0).getTextContent());
            }
        } catch (ParserConfigurationException
                | SAXException
                | IOException
                | XPathExpressionException e) {
            e.printStackTrace();
        } finally {
            try {
                // 关闭读取xml文件的输入流；
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 使用 XMLStreamWriter 创建 xml 文档：
     */
    public void schemeSeven() {
        try {
            XMLStreamWriter xsw = XMLOutputFactory.newInstance().createXMLStreamWriter(System.out);
            xsw.writeStartDocument("UTF-8", "1.0");
            xsw.writeEndDocument();
            String ns = "http://11:dd";
            xsw.writeStartElement("nsadfsadf", "person", ns);
            xsw.writeStartElement(ns, "id");
            xsw.writeCharacters("1");
            xsw.writeEndElement();
            xsw.writeEndElement();
            xsw.flush();
            xsw.close();
        } catch (XMLStreamException | FactoryConfigurationError e) {
            e.printStackTrace();
        }
    }

    /**
     * 通过 Transformer 更新节点信息；
     */
    public void schemeEight(String fileName) {
        InputStream is = null;
        try {
            is = StreamParseXml.class.getClassLoader().getResourceAsStream(fileName);
            // 创建文档处理对象
            DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            // 通过DocumentBuilder创建doc的文档对象
            assert is != null;
            Document doc = db.parse(is);
            // 创建XPath
            XPath xpath = XPathFactory.newInstance().newXPath();
            Transformer tran = TransformerFactory.newInstance().newTransformer();
            tran.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
            tran.setOutputProperty(OutputKeys.INDENT, "yes");
            // 第一个参数就是xpath,第二参数就是文档
            NodeList list =
                    (NodeList) xpath.evaluate("//book[title='Learning XML']", doc, XPathConstants.NODESET);
            // 获取price节点
            Element be = (Element) list.item(0);
            Element e = (Element) (be.getElementsByTagName("price").item(0));
            e.setTextContent("333.9");
            // 通过输出流创建一个结果；
            Result result = new StreamResult(System.out);
            // 通过tranformer修改节点
            tran.transform(new DOMSource(doc), result);
        } catch (ParserConfigurationException
                | XPathExpressionException
                | IOException
                | TransformerFactoryConfigurationError
                | TransformerException
                | SAXException e) {
            e.printStackTrace();
        } finally {
            try {
                // 关闭读取xml文件的输入流；
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
